<!DOCTYPE html>
          <head>
        <meta charset="utf-8">
            
            <title>
                「WebSocket」使用Websocket实现消息推送(下) | ttdevs
            </title>
            <meta content="width=device-width, initial-scale=1" name="viewport">
            <meta name="theme-color" content="#4184f3">
            
            
            <link href="/favicon.ico" rel="icon"/>
            

            <link rel="stylesheet" href="/css/highlight.light.css">
            <link rel="stylesheet" href="/css/prism-customize.css">
            <link rel="stylesheet" href="/css/nav-icon.css">
            <link rel="stylesheet" href="/css/waves.min.css">
            <link rel="stylesheet" href="/css/jquery.tocify.css">
            <link rel="stylesheet" href="/css/main.css">
            <link rel="stylesheet" href="/css/nav-indicator.css">
            
  

  
  <!-- 谷歌统计 -->
  <script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-97465173-1', 'auto');
    ga('send', 'pageview');

  </script>
  
            </meta>
        </meta>
    </head>

    <body>
        <header>
            <!-- cover image or sth. -->
        </header>
        <div id="main" class="m-scene">
            
<div class="nav-wrapper">

    <div class="container">
        <nav>
            <div class="logo wave">
                <a href="/" id="logo">
                    ttdevs
                </a>
            </div>
            <div class="nav-toggle-icon" >
                <div class="material-hamburger">
                    <span>
                    </span>
                    <span>
                    </span>
                    <span>
                    </span>
                </div>
            </div>
            <div class="menu-wrapper">
                <div class="nav-indicator">
                </div>
                <ul class="menus">
                    
                     
                        <li>
                            <a class="wave " href="/">
                                首页
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/archives">
                                归档
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/about">
                                关于
                            </a>
                        </li>
                     
                    
                   
                </ul>
            </div>
        </nav>
    </div>
</div>
            <div class="container content">
                <div class="scene_element scene_element--fadein">
                    <div class="row">
    <div class="main">
        <article>
          
          <header class="post-header with-cover" style="background-image:url('/1970/01/01/[WebSocket]使用Websocket实现消息推送(下)/cover.jpg')" >
          
          </header>
          <h1 class="post-title">「WebSocket」使用Websocket实现消息推送(下)</h1>

          <section class="post-info">
            <span class="post-date">1970/01/01</span>
            
            <span class="post-category">
                <a class="article-category-link" href="/categories/技术/">技术</a>
            </span>
            
            
            <span class="post-tags">
              <ul class="post-tag-list"><li class="post-tag-list-item"><a class="post-tag-list-link" href="/tags/WebSocket/">WebSocket</a></li></ul>
            </span>
            
          </section>

          <section class="post-content">
            <h2 id="0x00-WebSocket"><a href="/1970/01/01/[WebSocket]使用Websocket实现消息推送(下)/#0x00-WebSocket" class="headerlink" title="0x00 WebSocket"></a>0x00 WebSocket</h2><p>上一篇使用 <a href="https://github.com/TooTallNate/Java-WebSocket" target="_blank" rel="external">Java-WebSocket</a> 写了一套 <code>WebSocket</code> 的Demo，这一篇着重分析下<code>WebSocket</code> 的一些实现细节，更加详细的协议细节可参考 <a href="https://datatracker.ietf.org/doc/rfc6455/" target="_blank" rel="external">RFC6455</a>。</p>
<h2 id="0x01-WebSocket协议"><a href="/1970/01/01/[WebSocket]使用Websocket实现消息推送(下)/#0x01-WebSocket协议" class="headerlink" title="0x01 WebSocket协议"></a>0x01 WebSocket协议</h2><ul>
<li><code>WebSocket</code> 协议:  可参考<a href="https://datatracker.ietf.org/doc/rfc6455/" target="_blank" rel="external">RFC6455</a></li>
<li>抓包工具：<code>Charles</code> 和 <code>WireShark</code></li>
<li>测试代码：参考上一篇Demo</li>
</ul>
<h2 id="0x02-数据传输"><a href="/1970/01/01/[WebSocket]使用Websocket实现消息推送(下)/#0x02-数据传输" class="headerlink" title="0x02 数据传输"></a>0x02 数据传输</h2><ul>
<li><p>连接和断开</p>
<p>  当 Client 向 Server 发起 ws 连接请求后，会先使用 HTTP 协议建立 TCP 长链接，通过抓包，我们会看到类似这样的信息：</p>
<p>  Request：</p>
<p>  <img src="http://img.blog.csdn.net/20160910174128237" alt="Charles Request"></p>
<p>  Response：</p>
<p>  <img src="http://img.blog.csdn.net/20160910175927683" alt="Charles Response"></p>
<p>  这里简单介绍几个关键的Header字段：</p>
<ul>
<li>upgrade:websocket</li>
<li>connection:Upgrade</li>
<li>sec-websocket-version:13</li>
<li>sec-websocket-key:xxxxxx</li>
<li><p>sec-websocket-accept:</p>
<p>这些字段的信息可参考<a href="http://www.jianshu.com/p/867274a5e054" target="_blank" rel="external">这里</a>。</p>
</li>
</ul>
</li>
<li><p>数据传输</p>
<p>  当 HTTP 连接建立之后，就可以通过这个长链接进行数据传输了。如果你使用 Charles 进行数据抓包的话，可以看到类似下图中这样的聊天内容：</p>
<p>  <img src="http://img.blog.csdn.net/20160910180038193" alt="Charles websocket Chat"></p>
<p>   在同一个 HTTP 请求中，我们看到不断有数据交互，而不是像之前的 HTTP 请求那样，每发送一条数据，都需要发起一次新的请求。</p>
<p>  下面我们来尝试分析一下。这里要用到Wireshark。可能你会为什么换了Wireshark而不用Charles了？原因很简单，Charles给我们呈现的是应用层最终的结果，而传输层的具体的数据包发送接收却不能展示，这个时候就需要借助Wireshark来对TCP数据包进行分析了。</p>
<p>  首先，查询 <a href="https://datatracker.ietf.org/doc/rfc6455/" target="_blank" rel="external">RFC6455</a>文档，我们得知 ws 协议的数据包格式如下：</p>
<p>  <img src="http://img.blog.csdn.net/20160910181202338" alt="RFC6455 Frame"></p>
<p>  有了这个参考，我们就可以进行数据分析了。下面是我的一次数据收发过程：客户端向服务端发送数据：123456789，然后服务端原样发回，抓包如下：</p>
<p>  Sender：</p>
<p>  <img src="http://img.blog.csdn.net/20160911195743125" alt="WireShark sender">    </p>
<p>  Receiver：</p>
<p>  <img src="http://img.blog.csdn.net/20160911195848240" alt="WireShark receiver"></p>
<p>  下面我们着重对这两个数据包进行分析。细心的你可能会发现，截图中有四条数据，如果你对TCP的传输有所了解，第二条和第三条是确认报文。</p>
<p>  我们先看下接收到数据。点击数据中的 Data(11bytes) 部分，下方是自动选择了数据部分：8109313233343536373839。（在此之前是TCP报文的Header部分，我们可以忽略）根据 ws 协议，我们将其展开：</p>
  <figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">81 09:  10000001 00001001 </div><div class="line">31 32:  00110001 00110010</div><div class="line">33 34:  00110011 00110100</div><div class="line">35 36:  00110101 00110110</div><div class="line">37 38:  00110111 00111000</div><div class="line">39   :  00111001</div><div class="line">``` 	</div><div class="line">0：1，这是最后一帧</div><div class="line">1～3：全为0</div><div class="line">4～7：001，附加数据帧</div><div class="line">8：0，PlayloadData未经过掩码</div><div class="line"> 9～15：0001001 = 9 &lt; 125，因此数据长度位9</div><div class="line"> 剩下的：313233343536373839即为数据 123456789</div><div class="line"></div><div class="line">再来看看发送的数据。点击 Data(15bytes) 部分，下方的数据部分：818911eb9db220d9ae8624ddaa8a28，展开：</div><div class="line"></div><div class="line">``` shell</div><div class="line">81 89 : 10000001 10001001 </div><div class="line">11 eb : 00010001 11101011</div><div class="line">9d b2 : 10011101 10110010</div><div class="line">20 d9 : 00100000 11011001</div><div class="line">ae 86 : 10101110 10000110</div><div class="line">24 dd : 00100100 11011101</div><div class="line">aa 8a : 10101010 10001010</div><div class="line">28    : 00101000</div></pre></td></tr></table></figure>
<p>  0：1，这是最后一帧<br>  1～3：全为0<br>  4～7：001，附加数据帧<br>  8：1，PlayloadData经过掩码，（所有的由客户端发往服务端的帧此数位都被设置成 1。）<br>   9～15：0001001 = 9 &lt; 125，因此数据长度位9<br>   11 eb 9d b2：掩码<br>   20 d9 ae 86 24 dd aa 8a 28：数据<br>  关于掩码的计算：<br>  <img src="http://img.blog.csdn.net/20160911203233422" alt="掩码的计算"></p>
</li>
<li><p>连接保持</p>
<p>  除了正常的数据传输之外，Socket编程还有一个重要的组成部分：心跳保持。ws协议中定义了专门的数据包：</p>
<blockquote>
<p>  Control frames are identified by opcodes where the most significant bit of the opcode is 1.  Currently defined opcodes for control frames include 0x8 (Close), 0x9 (Ping), and 0xA (Pong).  Opcodes 0xB-0xF are reserved for further control frames yet to be defined.<br>Control frames are used to communicate state about the WebSocket. Control frames can be interjected in the middle of a fragmented message.<br>All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.</p>
</blockquote>
</li>
<li><p>其他</p>
<p>  一个协议不可能就这么简单，如果你还想了解更多，可参考<a href="https://datatracker.ietf.org/doc/rfc6455/" target="_blank" rel="external">RFC6455</a>。</p>
</li>
</ul>
<h2 id="0xFF-参考"><a href="/1970/01/01/[WebSocket]使用Websocket实现消息推送(下)/#0xFF-参考" class="headerlink" title="0xFF 参考"></a>0xFF 参考</h2><ol>
<li><a href="http://www.jianshu.com/p/867274a5e054" target="_blank" rel="external">http://www.jianshu.com/p/867274a5e054</a></li>
<li><a href="http://www.jianshu.com/p/fc09b0899141" target="_blank" rel="external">http://www.jianshu.com/p/fc09b0899141</a></li>
<li><a href="https://datatracker.ietf.org/doc/rfc6455/" target="_blank" rel="external">https://datatracker.ietf.org/doc/rfc6455/</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/WebSockets/Writing_WebSocket_servers" target="_blank" rel="external">https://developer.mozilla.org/zh-CN/docs/WebSockets/Writing_WebSocket_servers</a></li>
</ol>
<hr>
<p><img src="https://raw.githubusercontent.com/ttdevs/ttdevs.github.io/common/images/logo.png" alt="Create by ttdevs"></p>

          </section>
        </article>
        

       
        <div class="pager">
          
            <a class="post-prev pager-item" href="/1970/01/01/[macOS]Mac一句话分享/" >
              <strong class="article-nav-caption">上一篇</strong>
              <p class="post-nav-title">「macOS」Mac一句话分享</p>
            </a>
          
          
            <a class="post-next pager-item" href="/1970/01/01/[WebSocket]使用Websocket实现消息推送(心跳)/">
              <strong class="article-nav-caption">下一篇</strong>
              <p class="post-nav-title">「WebSocket」使用Websocket实现消息推送(心跳)</p>
            </a>
          
        </div>
        

         <!-- comments -->
        <div class="comment-section">
  
    


</div>

    </div>
    
    <aside>
        <div id="toc">
        </div>
    </aside>
    
</div>

                </div>
            </div>
        </div>
        <footer class="footer">
    <p>由<a href="http://hexo.io/" target="_blank">Hexo</a>强力驱动，搭载<a href="https://github.com/wayou/hexo-theme-gstyle">gstyle</a>主题</p>
    <p>
        &copy; 2017 ttdevs
    </p>
</footer>
<script src="/lib/jquery.js"></script>
<script src="/lib/waves.js"></script>
<script src="/lib/jquery-ui.js"></script>
<script src="/lib/jquery.tocify.js"></script>
<script src="/js/main.js"></script>

    </body>
</html>
